package org.dreamwork.floatingball.ui;

import android.app.Instrumentation;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;

import org.dreamwork.floatingball.activity.MainActivity;
import org.dreamwork.floatingball.R;
import org.dreamwork.floatingball.util.ViewManager;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;

import static org.dreamwork.floatingball.service.FloatingBallService2.D;

/**
 * Created by seth.yang on 2017/3/31
 */

public class      FloatingBall
       extends    LinearLayout
       implements View.OnClickListener, View.OnLongClickListener {
    public static final int HEIGHT = 64;
    private static final String TAG = "FloatingBall";

    private boolean dragging, collapsed = true;
    private InternalExecutor executor = new InternalExecutor ();
    private InternalTimer timer;
    private ImageView btnBack; //btnHome, , btnMenu, btnSettings, btnPowerOff, btnQuit;
    private View[] group;

    private Handler handler = new Handler ();

    private IViewSizeChangeListener listener;

    public void setViewSizeChangedListener (IViewSizeChangeListener listener) {
        this.listener = listener;
    }

    public FloatingBall (Context context) {
        super (context);
    }

    public FloatingBall (Context context, AttributeSet attrs) {
        super (context, attrs);
    }

    public FloatingBall (Context context, AttributeSet attrs, int defStyleAttr) {
        super (context, attrs, defStyleAttr);
    }

    public void setPool (ExecutorService pool) {
        pool.execute (executor);
        pool.execute (timer);
    }

    public boolean isDragging () {
        return dragging;
    }

    public void setDragging (boolean dragging) {
        this.dragging = dragging;
        timer.touch ();
    }

    public boolean isCollapsed () {
        return collapsed;
    }

    public int getCurrentWidth () {
        return collapsed ? HEIGHT : HEIGHT * 6;
    }

    public void guiInit () {
        btnBack = (ImageView) findViewById (R.id.btn_back);
        btnBack.setOnClickListener (this);
        btnBack.setOnLongClickListener (this);

        group = new View[] {
                findViewById (R.id.btn_home),
                findViewById (R.id.btn_menu),
                findViewById (R.id.btn_quit),
                findViewById (R.id.btn_poweroff),
                findViewById (R.id.btn_setting)
        };
        for (View tv : group) {
            tv.setOnClickListener (this);
        }

        timer = new InternalTimer (this, handler);
    }

    @Override
    protected void onMeasure (int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure (widthMeasureSpec, heightMeasureSpec);
        int width = getMeasuredWidth ();
        setMeasuredDimension (width, HEIGHT);
    }

    @Override
    public void setOnTouchListener (OnTouchListener l) {
        super.setOnTouchListener (l);
        btnBack.setOnTouchListener (l);
        for (View tv : group) {
            tv.setOnTouchListener (l);
        }
    }

    @Override
    public void onClick (View v) {
        if (isDragging ()) {
            return;
        }
        timer.touch ();
        switch (v.getId ()) {
            case R.id.btn_back :
                if (D)
                    Log.d (TAG, "back pressed");
                executor.send (KeyEvent.KEYCODE_BACK);
                break;
            case R.id.btn_home :
                if (D)
                    Log.d (TAG, "home pressed");
                executor.send (KeyEvent.KEYCODE_HOME);
                break;
            case R.id.btn_menu :
                if (D)
                    Log.d (TAG, "menu pressed");
                executor.send (KeyEvent.KEYCODE_MENU);
                break;
            case R.id.btn_quit :
                if (D)
                    Log.d (TAG, "quit pressed");
                toggle ();
                break;
            case R.id.btn_setting :
                if (D)
                    Log.d (TAG, "setting pressed");
                onSettings ();
                break;
            case R.id.btn_poweroff :
                onPowerOff ();
                break;
        }
    }

    @Override
    public boolean onLongClick (View v) {
        timer.touch ();
        if (v.getId () == R.id.btn_back) {
            toggle ();
        }
        return true;
    }

    private void toggle () {
        if (collapsed) {
            for (View tv : group) {
                tv.setVisibility (VISIBLE);
            }
            timer.proceed ();
        } else {
            for (View tv : group) {
                tv.setVisibility (GONE);
            }
        }
        collapsed = !collapsed;

        if (listener != null) {
            if (D) {
                Log.d (TAG, "view size changed");
            }
            listener.notifyViewSizeChanged (this);
        }
    }

    private void onSettings () {
        Context ctx = getContext ();
        Intent intent = new Intent (ctx, MainActivity.class);
        intent.setFlags (Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY);
        ctx.startActivity (intent);
    }

    private void onPowerOff() {
        Context ctx = getContext ();
        LayoutInflater inflater = (LayoutInflater)ctx.getSystemService (Service.LAYOUT_INFLATER_SERVICE);
        ConfirmPanel panel = (ConfirmPanel) inflater.inflate (R.layout.confirm_dialog, null);
        panel.guiInit ();
        ViewManager.getInstance ().showDialog ();
    }

    public void clear () {
        if (timer != null) {
            timer.shutdown ();
            timer = null;
        }

        if (executor != null) {
            executor.running = false;
            executor = null;
        }
    }

    private static final class InternalExecutor implements Runnable {
        BlockingQueue<Integer> queue = new LinkedBlockingQueue<> ();
        boolean running = true;

        @Override
        public void run () {
            if (D) {
                Log.d (TAG, "internal executor startup");
            }

            Instrumentation inst = new Instrumentation ();
            while (running) {
                try {
                    Integer code = queue.take ();
                    if (D) {
                        Log.d (TAG, "got a keycode: " + code);
                    }
                    if (code == -1) {
                        running = false;
                        break;
                    }
                    inst.sendKeyDownUpSync (code);
                } catch (InterruptedException e) {
                    e.printStackTrace ();
                }
            }

            queue.clear ();
            if (D) {
                Log.d (TAG, "internal executor shutdown.");
            }
        }

        void send (int keycode) {
            queue.offer (keycode);
        }
    }

    private static final class InternalTimer implements Runnable {
        static final long TIMEOUT = 10000;
        final Object LOCKER = new byte [0];
        long timestamp;
        boolean running = true, halt = true;
        FloatingBall ball;
        Handler handler;

        InternalTimer (FloatingBall ball, Handler handler) {
            this.ball = ball;
            this.handler = handler;
        }

        @Override
        public void run () {
            if (D) {
                Log.d (TAG, "timer start.");
            }
            while (running) {
                if (halt) {
                    synchronized (LOCKER) {
                        try {
                            if (D) {
                                Log.d (TAG, "halt the timer");
                            }
                            LOCKER.wait ();
                            if (D) {
                                Log.d (TAG, "the timer release...");
                            }
                        } catch (InterruptedException e) {
                            e.printStackTrace ();
                        }
                    }
                }

                if (running) {
                    if (System.currentTimeMillis () - timestamp > TIMEOUT) {
                        if (D) {
                            Log.d (TAG, "timeout, process it.");
                        }
                        handler.post (new Runnable () {
                            @Override
                            public void run () {
                                ball.toggle ();
                            }
                        });
                        halt = true;
                    }

                    try {
                        Thread.sleep (10);
                    } catch (InterruptedException e) {
                        e.printStackTrace ();
                    }
                }
            }

            if (D) {
                Log.d (TAG, "internal timer shutdown.");
            }
        }

        void touch () {
            timestamp = System.currentTimeMillis ();
        }

        void proceed () {
            synchronized (LOCKER) {
                halt = false;
                LOCKER.notifyAll ();
            }
        }

        void shutdown () {
            proceed ();
            running = false;
        }
    }

    public interface IViewSizeChangeListener {
        void notifyViewSizeChanged (FloatingBall ball);
    }
}